package com.wfh.ratelimter.aspect;


import com.wfh.common.ErrorCode;
import com.wfh.exception.BusinessException;
import com.wfh.inner.InnerUserService;
import com.wfh.model.entity.User;
import com.wfh.ratelimter.annotation.RateLimit;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RRateLimiter;
import org.redisson.api.RateIntervalUnit;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;
import java.time.Duration;

/**
 * @Title: RateLimtterAspect
 * @Author wangfenghuan
 * @Package com.wfh.aigenerator.ratelimter.aspect
 * @Date 2025/8/13 21:34
 * @description:
 */
@Aspect
@Component
@Slf4j
public class RateLimtterAspect {

    @Resource
    private RedissonClient redissonClient;

    @Resource
    @Lazy
    private InnerUserService userService;

    @Before("@annotation(rateLimit)")
    public void doBefore(JoinPoint point, RateLimit rateLimit) {
        String key = generateRateLimitKey(point, rateLimit);
        // 使用Redisson的分布式限流器
        RRateLimiter rateLimiter = redissonClient.getRateLimiter(key);
        rateLimiter.expire(Duration.ofHours(1)); // 1 小时后过期
        // 设置限流器参数：每个时间窗口允许的请求数和时间窗口
        rateLimiter.trySetRate(RateType.OVERALL, rateLimit.rate(), rateLimit.rateInterval(), RateIntervalUnit.SECONDS);
        // 尝试获取令牌，如果获取失败则限流
        if (!rateLimiter.tryAcquire(1)) {
            throw new BusinessException(ErrorCode.TOO_MANY_REQUEST, rateLimit.message());
        }
    }

    /**
     * 获取限流key
     * @param point
     * @param rateLimit
     * @return
     */
    private String generateRateLimitKey(JoinPoint point, RateLimit rateLimit) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("rate_limit:");
        // 添加自定义的前缀
        if (!rateLimit.key().isEmpty()) {
            stringBuilder.append(rateLimit.key()).append(":");
        }
        // 根据限流的类型生成不同的key
        switch (rateLimit.limitType()) {
            case API ->{
                // 接口级别，方法名称
                MethodSignature signature = (MethodSignature) point.getSignature();
                Method method = signature.getMethod();
                stringBuilder.append("api:").append(method.getDeclaringClass().getSimpleName())
                        .append(".").append(method.getName());
            }
            case USER -> {
                try {
                    // 用户级别，用户id
                    ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                    if (attributes != null){
                        HttpServletRequest request = attributes.getRequest();
                        User userId = InnerUserService.getLoginUser(request);
                        stringBuilder.append("user:").append(userId.getId());
                    }else {
                        // 无法获取请求上下文，使用ip进行限流
                        stringBuilder.append("ip:").append(getClientIP());
                    }
                } catch (Exception e) {
                    // 没有登录用户使用ip进行限流
                    stringBuilder.append("ip:").append(getClientIP());
            }
        }
        case IP -> {
                // ip级别，ip地址
                stringBuilder.append("ip:").append(getClientIP());
        }
        default -> {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "不支持的限流类型");
            }
        }
        return stringBuilder.toString();
    }

    /**
     * 获取ip
     * @return
     */
    private String getClientIP() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) {
            return "unknown";
        }
        HttpServletRequest request = attributes.getRequest();
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        // 处理多级代理的情况
        if (ip != null && ip.contains(",")) {
            ip = ip.split(",")[0].trim();
        }
        return ip != null ? ip : "unknown";
    }


}
